# 通过轮廓检测方式实现缺陷检测

import cv2
import numpy

# 加载药片图像
image_bgr = cv2.imread("images/troche.png")
if image_bgr is None:
    print("imread error\n")
    exit(1)

image_gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
cv2.imshow("image_gray", image_gray)
cv2.waitKey()

ret_threshold, image_binary = cv2.threshold(image_gray, 128.0, 255.0, cv2.THRESH_BINARY)
cv2.imshow("image_binary", image_binary)
cv2.waitKey()

# 获取用以后续形态学操作的结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,9))

# 腐蚀
image_erode = cv2.morphologyEx(image_binary, cv2.MORPH_ERODE, kernel)
cv2.imshow("image_erode", image_erode)
# # 开运算(先腐蚀再膨胀)
# image_open = cv2.morphologyEx(image_binary, cv2.MORPH_OPEN, kernel)
# cv2.imshow("image_open", image_open)
cv2.waitKey()

# 若图像里的多个对象没有连接在一起，则可以直接通过腐蚀操作分离出多个对象
# f否则，则很难通过腐蚀分离出多个对象。这时，可以使用distanceTransform函数提取对象
# distanceTransform函数的作用：计算二值图像内任意点到最近背景点(像素值为0)的距离。对象质心距离背景较远，结果数值较大；对象边缘距离背景较近，结果数值较小。
distance_mat = cv2.distanceTransform(image_binary, cv2.DIST_L1, 3)

# 用最大距离的0.5作为阈值分割出前景图像
ret_threshold, image_foregroud_float = cv2.threshold(distance_mat, 0.5 * distance_mat.max(), 255.0, cv2.THRESH_BINARY)
image_foregroud = image_foregroud_float.astype(numpy.uint8)
# 显示前景图像
cv2.imshow("image_foregroud", image_foregroud)
cv2.waitKey()

# 以前景图像作为根据进行处理

# 查找前景图的对象轮廓,返回轮廓集合，轮廓层级关系
contours, hierarchy = cv2.findContours(image_foregroud, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

efect_count = 0
# 遍历轮廓
for contour in contours:
    # 计算轮廓面积
    contour_area  = cv2.contourArea(contour)

    # 获取轮廓的最小外接圆,返回中心点和半径
    center, radius = cv2.minEnclosingCircle(contour)
    x = int(center[0])
    y = int(center[1])
    radius_int = int(radius)
    closing_circle_area = 3.14 * radius * radius

    # 画出最小外接圆
    cv2.circle(image_foregroud, (x, y), radius_int, (255, 255, 255), 1)

    # 若轮廓面积占最小外接圆面积超过70%，则认为是有效药片
    if contour_area / closing_circle_area > 0.7:
        efect_count = efect_count + 1

print("efect_count=%ld" % efect_count)
cv2.imshow("foregroud_image-add-circle", image_foregroud)
cv2.waitKey()

cv2.destroyAllWindows()
exit(0)